#include "proc.h"
#include "fs/fat32.h"
#include "fs/file.h"
#include "lock.h"
#include "mmu.h"
#include "mmulayout.h"
#include "riscv.h"
#include "stddef.h"
#include "stdio.h"
#include "string.h"
#include "trap.h"
#include "type.h"

#include "dbg.h"

extern void contentSwitch(context_t*, context_t*);

// proc_t proc[NPROC];
// cpu_t cpu[NCPUS];
// spinlock_t pid_lk;
// pid_t pid = 0;

int runOver = TRUE;
proc_t proc[NPROC];
cpu_t cpu[NCPUS];
spinlock_t pid_lk;
pid_t pid = 0;

extern dirent_t root;

// code_t initcode[] = {
//     0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02, 0x93, 0x08,
//     0x20, 0x00, 0x73, 0x00, 0x00, 0x00, 0x93, 0x08, 0x20, 0x00, 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0x9f, 0xff,
//     0x2f, 0x69, 0x6e, 0x64, 0x74, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// };
code_t initcode[] = {
    // 0x05, 0xd0, 0x08, 0x93,
    // 0x00, 0x00, 0x00, 0x73,
    0x93, 0x08, 0xd0, 0x05, 0x73, 0x00, 0x00, 0x00,
};
void
proc_init(void) {
    __DEBUG_FUNC_START;
    proc_t* p;
    spinlock_init(&pid_lk, "pid");  // TODO自旋锁？
    for (p = proc; p < &proc[NPROC]; p++) {
        p->state = UNUSED;
        spinlock_init(&p->lk, "proc");
    }
    cpu[0].lk_num = 0;
    cpu[1].lk_num = 0;
    cpu[0].current_p = NULL;
    cpu[1].current_p = NULL;
    __DEBUG_FUNC_END;
}

proc_t*
getCurrentProc(void) {
    intr_push();
    proc_t* p = getCurrentCPU()->current_p;
    intr_pop();
    return p;
}

pid_t
alloc_pid(void) {
    uint32_t c_pid;
    spinlock_acquire(&pid_lk);
    pid++;
    c_pid = pid;
    spinlock_release(&pid_lk);
    return c_pid;
}

void
entry_user_space(void) {
    static int flag = 0;
    runOver = FALSE;
    proc_t* p = getCurrentProc();
    spinlock_release(&p->lk);
    if (flag == 0) {
        // runOver = FALSE;
        fat32_init();
        flag = 1;
        // runOver = TRUE;
    } else {
        elf_load(p, p->path);
        // runOver = TRUE;
    }
    usertrapret();
}

proc_t*
alloc_proc(void) {
    proc_t* p;
    for (p = proc; p < proc + NPROC; ++p) {
        spinlock_acquire(&p->lk);
        if (p->state == UNUSED)
            break;
        spinlock_release(&p->lk);
    }
    // TODO p可能没找到，后续赋值非法
    p->state = NEW;
    spinlock_release(&p->lk);
    p->pid = alloc_pid();
    alloc_proc_mem(p);
    p->kstack = (void*)UVK_STACK;
    memset(&p->context, 0, sizeof(p->context));
    p->context.ra = (uint64_t)entry_user_space;
    p->context.sp = (address_t)p->kstack + PAGE_SIZE;
    // p->cwd = __fat32_name("/");
    p->cwd = &root;
    p->openFiles[STDIN] = (file_t*)STDIN_PTR;
    p->openFiles[STDOUT] = (file_t*)STDOUT_PTR;
    p->openFiles[STDERR] = (file_t*)STDERR_PTR;
    p->isForked = 0;
    p->heap = NULL;
    p->chan = NULL;
    p->tms.tms_cstime = 0;
    p->tms.tms_cutime = 0;
    p->tms.tms_stime = 0;
    p->tms.tms_utime = 0;
    p->tms.entry_kernel = 0;
    p->tms.leave_kernel = 0;
    return p;
}

void
creat_first_proc(void) {
    __DEBUG_FUNC_START;
    proc_t* p;
    p = alloc_proc();
    uvminit(p->upageTable, p->kpageTable, initcode, (uint64_t)sizeof(initcode));
    p->trapFrame->sp = PAGE_SIZE;
    p->trapFrame->u_pc = 0x0;
    p->state = READY;
    // printf("creat_first+proc done\n");
    __DEBUG_FUNC_END;
}

void
freeproc(proc_t* p) {
    if (p->trapFrame)
        kfree((void*)p->trapFrame);
    p->trapFrame = NULL;
    if (p->kpageTable) {
        kvmfree(p->kpageTable);
    }
    p->kpageTable = NULL;
    if (p->upageTable) {
        uvmfree(p);
    }
    p->upageTable = NULL;
    p->size = 0;
    p->pid = 0;
    p->parent = 0;
    p->chan = NULL;
    p->xstatus = 0;
    p->heap = NULL;
    p->state = UNUSED;
}

void
creat_proc(char* path) {
    if (path == NULL) {
        printf("execute over\n");
        while (1)
            ;
    }
    runOver = FALSE;
    proc_t* p = alloc_proc();
    for (int i = 0; i < PRO_SIZE; i += PAGE_SIZE) {
        void* mem = kalloc();
        memset(mem, 0, PAGE_SIZE);
        create_pages_map(p->upageTable, i, (address_t)mem, PAGE_SIZE, PTE_R | PTE_W | PTE_X | PTE_U);
        create_pages_map(p->kpageTable, i, (address_t)mem, PAGE_SIZE, PTE_R | PTE_W | PTE_X);
    }
    void* stack_mem = kalloc();
    memset(stack_mem, 0, PAGE_SIZE);
    create_pages_map(p->upageTable, (address_t)PRO_SIZE, (address_t)stack_mem, PAGE_SIZE, PTE_R | PTE_W | PTE_U);
    p->size = PRO_SIZE + PAGE_SIZE;
    //  elf_load(p, path);
    p->path = path;
    p->trapFrame->sp = p->size - PAGE_SIZE;
    p->state = READY;
}

void
scheduler(void) {
    proc_t* p;
    extern pageTable_t kernel_pagetable;
    cpu_t* c = getCurrentCPU();
    uint32_t isFind = 0;
    char* argv[] = {// "dup", "dup2", "fstat", "getpid", "open", "read", "times",
                    // "gettimeofday",  // assert(test_ret >= 0); 失败, 但是返回值为 a0=2748136
                    //"getppid", proc->parent=NULL, 导致返回值为0, 客户程序报错
                    //"getcwd", 直到sys_getcwd返回, 一切正常, 后续不知是在怎么执行, 最后panic("dentry_lock")
                    "openat",  //创建文件存在错误
                    // "getdents", sys_write的时候报错，应该是TEST_START(__func__)对应的输出, scause=0xd,
                    // 0x1f20 <error: Cannot access memory at address 0x1f20> 问题是传入的buf对应地址无法访问 ,
                    // "mkdir_", 依赖创建文件
                    // "chdir", 依赖mkdir
                    // "uname", 与getdents情况一样
                    //"mmap", 同uname
                    // "write", 直接 panic: dentry_lock
                    // "yield", 直接 panic: dentry_lock
                    NULL};
    int i = 0;

    while (1) {
        isFind = 0;
        intr_on();
        for (p = proc; p < &proc[NPROC]; p++) {
            spinlock_acquire(&p->lk);
            if (p->state == READY) {
                isFind = 1;
                p->state = RUNNING;
                // spinlock_release(&p->lk);
                getCurrentCPU()->current_p = p;
                p->tms.entry_kernel = r_time();
                w_satp(PAGE_TABLE_SHIFT(p->kpageTable));
                sfenve_vma();
                contentSwitch(&(c->context), &(p->context));
                w_satp(PAGE_TABLE_SHIFT(kernel_pagetable));
                sfenve_vma();
                p = proc;
                proc_t* nowp = getCurrentProc();
                if ((nowp->state == ZOMBIE) && (nowp->isForked == 0))
                    freeproc(nowp);
                isFind = 0;
                getCurrentCPU()->current_p = NULL;
            }
            if (spinlock_isHold(&p->lk))
                spinlock_release(&p->lk);
        }
        if (isFind == 0 && runOver) {
            creat_proc(argv[i++]);
        }
    }
    while (1) {
        printf("%s while(1)\n", __func__);
    }
}

cpu_t*
getCurrentCPU(void) {
    return &cpu[r_tp()];
}

void
yield(void) {
#ifdef DEBUG
    printf("yield");
#endif
    proc_t* p = getCurrentProc();
    spinlock_acquire(&p->lk);
    p->state = READY;
    sched();
    spinlock_release(&p->lk);
}

void
sched(void) {
    proc_t* p = getCurrentProc();
    contentSwitch(&p->context, &getCurrentCPU()->context);
}

// void
// sleep_sem(void* chan, semaphore_t* lk) {
//     proc_t* p = getCurrentProc();
//     if (lk != &p->lk) {
//         semaphore_acquire(&p->lk);
//         semaphore_release(lk);
//     } else
//         semaphore_release(&p->lk);

//     p->chan = chan;
//     p->state = SLEEPING;

//     sched();

//     p->chan = 0;

//     if (lk != &p->lk) {
//         semaphore_release(&p->lk);
//         semaphore_acquire(lk);
//     }
// }

void
sleep_spin(void* chan, spinlock_t* lk) {
    proc_t* p = getCurrentProc();
    if (lk != &p->lk) {
        spinlock_acquire(&p->lk);
        spinlock_release(lk);
    }
    // else
    // spinlock_release(&p->lk);

    p->chan = chan;
    p->state = SLEEPING;

    sched();

    p->chan = 0;

    if (lk != &p->lk) {
        spinlock_release(&p->lk);
        spinlock_acquire(lk);
    }
}

// void
// proc_wakeupSem(void* chan) {
//     proc_t* p;
//     for (p = proc; p < &proc[NPROC]; p++) {
//         semaphore_acquire(&p->lk);
//         if (p->state == SLEEPING && p->chan == chan) {
//             p->state = READY;
//         }
//         semaphore_release(&p->lk);
//     }
// }

void
proc_wakeupSpin(void* chan) {
    proc_t* p;
    for (p = proc; p < &proc[NPROC]; p++) {
        spinlock_acquire(&p->lk);
        if (p->state == SLEEPING && p->chan == chan) {
            //   __DEBUG_INFO(found);
            p->state = READY;
        }
        spinlock_release(&p->lk);
    }
}

void
proc_wakeup(proc_t* w_proc) {
    if ((w_proc >= proc) && (w_proc < &proc[NPROC]) && (w_proc->state == SLEEPING)) {
        w_proc->state = READY;
    } else {
        panic("proc wakeup : unknown proc\n");
        while (1)
            ;
    }
}

pid_t
wait(pid_t pid) {
    proc_t* p = getCurrentProc();
    spinlock_acquire(&p->lk);
    int havekids = 0;
    if (pid == -1) {
        while (1) {
            for (proc_t* child_p = proc; child_p < &proc[NPROC]; child_p++) {
                if (child_p->parent == p) {
                    havekids = 1;
                    spinlock_acquire(&child_p->lk);
                    if (child_p->state == ZOMBIE) {
                        freeproc(child_p);
                        spinlock_release(&child_p->lk);
                        spinlock_release(&p->lk);
                    }
                    spinlock_release(&child_p->lk);
                }
            }
            if (!havekids) {
                spinlock_release(&p->lk);
            } else {
                sleep_spin(p, &p->lk);
            }
        }
    } else {
        while (1) {
            for (proc_t* np = proc; np < &proc[NPROC]; np++) {
                if (np->pid == pid) {
                    havekids = 1;
                    spinlock_acquire(&np->lk);
                    if (np->state == ZOMBIE) {
                        pid_t pid = p->pid;
                        freeproc(np);
                        spinlock_release(&np->lk);
                        spinlock_release(&p->lk);
                        return pid;
                    }
                    spinlock_release(&np->lk);
                    break;
                }
            }
            if (!havekids) {
                spinlock_release(&p->lk);
            } else {
                sleep_spin(p, &p->lk);
            }
        }
    }
    return p->pid;
}

int
fork(void) {
    proc_t* newp = alloc_proc();
    proc_t* p = getCurrentProc();
    uvmcopy(p, newp);
    newp->parent = p;
    newp->trapFrame->a0 = 0;
    newp->isForked = 1;
    newp->state = READY;
    return newp->pid;
}

void
reparent(proc_t* p) {
    proc_t* parent_p = p->parent;
    if (p->parent != NULL) {
        p->parent = parent_p->parent;
    }
}
